﻿using GalaSoft.MvvmLight.CommandWpf;
using Mimi.Common;
using Mimi.Models.DataModels;
using Mimi.Models.UIModels;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace Mimi.ViewModels
{
    public class BudgetViewModel : BindingBase, IBudgetViewModel
    {
        #region Fields
        private IExpenseViewModel expenseViewModel;
        private IOverviewViewModel overviewViewModel;
        private IStorageManager storageManager;
        #endregion

        #region Properties
        private double budgetPercentage;
        /// <summary>
        /// <see cref="TotalBudgetPercentage"/>
        /// </summary>
        public double TotalBudgetPercentage
        {
            get { return budgetPercentage; }
            set
            {
                Set(ref budgetPercentage, value);
            }
        }

        private double budget;
        /// <summary>
        /// <see cref="TotalBudget"/>
        /// </summary>
        public double TotalBudget
        {
            get { return budget; }
            set
            {
                Set(ref budget, value);
            }
        }

        private List<Purpose> purposes;
        /// <summary>
        /// <see cref="Purposes"/>
        /// </summary>
        public List<Purpose> Purposes
        {
            get { return purposes ?? (purposes = new List<Purpose>()); }
            set
            {
                Set(ref purposes, value);
            }
        }

        private ObservableCollection<UIPurpose> bindingPurposeCollection;
        /// <summary>
        /// <see cref="BindingPurposeCollection"/>
        /// </summary>
        public ObservableCollection<UIPurpose> BindingPurposeCollection
        {
            get { return bindingPurposeCollection ?? (bindingPurposeCollection = new ObservableCollection<UIPurpose>()); }
            set
            {
                Set(ref bindingPurposeCollection, value);
            }
        }

        private ObservableCollection<string> bindingValueType;
        /// <summary>
        /// <see cref="BindingValueType"/>
        /// </summary>
        public ObservableCollection<string> BindingValueType
        {
            get { return bindingValueType ?? (bindingValueType = new ObservableCollection<string>()); }
            set
            {
                Set(ref bindingValueType, value);
            }
        }

        private string selectedValueType;
        /// <summary>
        /// <see cref="SelectedValueType"/>
        /// </summary>
        public string SelectedValueType
        {
            get { return selectedValueType; }
            set
            {
                Set(ref selectedValueType, value);

                if (selectedValueType == "%")
                {
                    if (!string.IsNullOrEmpty(Quota) && double.Parse(Quota) > 100)
                    {
                        Quota = "100";
                    }
                }
            }
        }

        private string description;
        /// <summary>
        /// <see cref="Description"/>
        /// </summary>
        public string Description
        {
            get { return description; }
            set
            {
                Set(ref description, value);
            }
        }

        private string quota;
        /// <summary>
        /// <see cref="Quota"/>
        /// </summary>
        public string Quota
        {
            get { return quota; }
            set
            {
                bool isValid = CheckValidAmount(value);

                if (isValid || string.IsNullOrEmpty(value))
                {
                    if (value.StartsWith("0."))
                    {
                        Set(ref quota, value);
                    }
                    else if (value.StartsWith("0") && value.Length > 1)
                    {
                        Set(ref quota, value.Remove(0, 1));
                    }
                    else if (this.SelectedValueType == "%" && !string.IsNullOrEmpty(value) && double.Parse(value) > 100)
                    {
                        Set(ref quota, "100");
                    }
                    else
                    {
                        Set(ref quota, value);
                    }
                }
                else
                {
                    Set(ref quota, "0");
                }
            }
        }

        private Currency defaultCurrency;
        /// <summary>
        /// <see cref="DefaultCurrency"/>
        /// </summary>
        public Currency DefaultCurrency
        {
            get { return defaultCurrency; }
            set
            {
                Set(ref defaultCurrency, value);
            }
        }
        #endregion

        #region Commands
        private RelayCommand addNewBudgetCommand;
        /// <summary>
        /// <see cref="AddNewBudgetCommand"/>
        /// </summary>
        public RelayCommand AddNewBudgetCommand
        {
            get { return addNewBudgetCommand ?? (addNewBudgetCommand = new RelayCommand(AddNewBudget)); }
        }

        private RelayCommand resetInputCommand;
        /// <summary>
        /// <see cref="ResetInputCommand"/>
        /// </summary>
        public RelayCommand ResetInputCommand
        {
            get { return resetInputCommand ?? (resetInputCommand = new RelayCommand(ResetInputData)); }
        }
        #endregion

        #region Methods
        /// <summary>
        /// Default Constructor
        /// </summary>
        public BudgetViewModel(IExpenseViewModel expenseViewModel, IOverviewViewModel overviewViewModel, IStorageManager storageManager)
        {
            this.expenseViewModel = expenseViewModel;
            this.overviewViewModel = overviewViewModel;
            this.storageManager = storageManager;

            //Init data
            LoadValueTypeList();
        }

        /// <summary>
        /// Load value types
        /// </summary>
        private void LoadValueTypeList()
        {
            BindingValueType.Clear();

            //Get default currency
            DefaultCurrency = overviewViewModel.DefaultCurrency;

            //Init value type list
            BindingValueType.Add("%");
        }

        /// <summary>
        /// <see cref="LoadStoredData"/>
        /// </summary>
        public async void LoadStoredData()
        {
            //Get local data
            IEnumerable<Purpose> localData = await storageManager.QueryAllBudgetData();

            foreach (Purpose item in localData)
            {
                this.Purposes.Add(item);
                this.BindingPurposeCollection.Add(new UIPurpose(item));

                //Update total budget
                this.TotalBudgetPercentage += item.Quota;
                this.TotalBudget += item.Budget;
            }

            //Import purpose list to ExpenseViewModel
            expenseViewModel.LoadPurposeList(this.Purposes);

            Debug.WriteLine("Budget completed");
        }

        /// <summary>
        /// <see cref="AddNewBudget"/>
        /// </summary>
        public void AddNewBudget()
        {
            if (string.IsNullOrEmpty(Quota) || string.IsNullOrEmpty(Description) || string.IsNullOrEmpty(this.SelectedValueType))
            {
                return;
            }

            double newQuota = double.Parse(Quota);

            if ((TotalBudgetPercentage + double.Parse(quota)) > 100)
            {
                MessageBox.Show("Total budget is over 100%");
                return;
            }

            //Add Budget to Local
            Purpose newPurpose = new Purpose();
            newPurpose.Quota = double.Parse(this.Quota);
            newPurpose.Description = this.Description;
            newPurpose.Type = this.SelectedValueType == "%" ? PurposeUnitValueType.Percentage : PurposeUnitValueType.Static;
            newPurpose.Currency = RetrieveCurrency(newPurpose);

            if (newPurpose.Type == PurposeUnitValueType.Percentage)
            {
                //Calculate budget for new purpose
                double availableAmount = (overviewViewModel.Balance - TotalBudget) / (100 - TotalBudgetPercentage);
                newPurpose.Budget = newPurpose.Quota * availableAmount;
            }
            else
            {
                newPurpose.Budget = newPurpose.Quota;
            }

            Purposes.Add(newPurpose);
            newPurpose.PurposeId = Purposes.Count.ToString("D10");

            //Add Budget to UI
            UIPurpose newUIPurpose = new UIPurpose();
            newUIPurpose.Quota = this.Quota;
            newUIPurpose.Description = this.Description;
            newUIPurpose.Type = this.SelectedValueType;
            newUIPurpose.Budget = newPurpose.Budget.ToString();
            newUIPurpose.Currency = RetrieveCurrency(newPurpose);
            newUIPurpose.PurposeId = newPurpose.PurposeId;

            BindingPurposeCollection.Add(newUIPurpose);

            //Update total budget
            this.TotalBudgetPercentage += newPurpose.Quota;
            this.TotalBudget += newPurpose.Budget;

            //Save to local
            storageManager.AddBudgetData(newPurpose);

            //Reset input
            ResetInputData();

            //Update purpose collection on ExpenseViewModel
            expenseViewModel.UpdatePurposeList(newPurpose);
        }

        /// <summary>
        /// Check the expense amount validation
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        private bool CheckValidAmount(string input)
        {
            double amount;

            bool result = double.TryParse(input, out amount);

            return result;
        }

        /// <summary>
        /// Reset inputs
        /// </summary>
        private void ResetInputData()
        {
            this.Quota = string.Empty;
            this.Description = string.Empty;
        }

        /// <summary>
        /// Retrieve Currency
        /// </summary>
        /// <param name="purposeItem"></param>
        /// <returns></returns>
        private Currency RetrieveCurrency(Purpose purposeItem)
        {
            return (purposeItem.Type == PurposeUnitValueType.Percentage) ? overviewViewModel.DefaultCurrency : (this.SelectedValueType == "$" ? Currency.USD : Currency.VND);
        }

        /// <summary>
        /// <see cref="UpdateBudgets"/>
        /// </summary>
        public void UpdateBudgets(Expense expense)
        {
            //Get budget item
            Purpose item = this.Purposes.FirstOrDefault((x) => x.PurposeId == expense.SpentOn.PurposeId);

            //Update budget
            if (item != null)
            {
                item.Budget -= expense.Amount;
                TotalBudget -= expense.Amount;
                
                //Update on UI
                UIPurpose uiItem = this.BindingPurposeCollection.FirstOrDefault((x) => x.PurposeId.Equals(item.PurposeId));

                if (uiItem != null)
                {
                    uiItem.Budget = item.Budget.ToString();
                }

                //Update local data
                storageManager.UpdateBudgetRowData(item);
            }
        }

        /// <summary>
        /// <see cref="UpdateBudgetsWithNewBalance"/>
        /// </summary>
        /// <param name="value"></param>
        public IEnumerable<Income> UpdateBudgetsWithNewBalance(double value)
        {
            List<Income> incomeDistribution = new List<Income>();

            double remaining = value;

            foreach (var purpose in Purposes)
            {
                double additionalValue = (value * purpose.Quota) / 100;
                purpose.Budget += additionalValue;
                TotalBudget += additionalValue;

                remaining -= additionalValue;

                incomeDistribution.Add(new Income() { Amount = additionalValue, Budget = purpose.Description, Date = DateTime.Now });
                UIPurpose uiItem = this.BindingPurposeCollection.FirstOrDefault((x) => x.PurposeId == purpose.PurposeId);

                if (uiItem != null)
                {
                    uiItem.Budget = purpose.Budget.ToString();
                }

                storageManager.UpdateBudgetRowData(purpose);
            }

            //if no budgets available
            if (incomeDistribution.Count == 0)
            {
                incomeDistribution.Add(new Income() { Amount = value, Budget = "Total", Date = DateTime.Now });
            }
            else if (remaining > 0)
            {
                incomeDistribution.Add(new Income() { Amount = remaining, Budget = "Total", Date = DateTime.Now });
            }

            return incomeDistribution;
        }
        #endregion
    }
}
